package org.hang.jvm.classpath;

import org.hang.jvm.classpath.impl.WildcardEntry;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Objects;

/**
 * 类路径
 *
 * @author 廖景航
 * @date created on 2022/2/23 下午3:59
 */
public class Classpath {

    /**
     * 启动类路径
     */
    private Entry bootstrapClasspathEntry;

    /**
     * 扩展类路径
     */
    private Entry extensionClasspathEntry;

    /**
     * 用户类路径
     */
    private Entry userClasspathEntry;

    public Classpath(String jreOption, String cpOption) {
        // 启动类或者扩展类，比如 ~/.jdks/adopt-openjdk-1.8.0_292/jre
        bootstrapAndExtensionClasspath(jreOption);
        // 用户类，比如项目目录
        parseUserClasspath(cpOption);
    }

    private void parseUserClasspath(String cpOption) {
        if (Objects.isNull(cpOption)) {
            cpOption = ".";
        }
        userClasspathEntry = Entry.create(cpOption);
    }

    private void bootstrapAndExtensionClasspath(String jreOption) {
        String jreDir = getJreDir(jreOption);
        // ../jre/lib/*
        String jreLibPath = Paths.get(jreDir, "lib") + File.separator + "*";
        bootstrapClasspathEntry = new WildcardEntry(jreLibPath);
        // ../jre/lib/ext/*
        String jreExtPath = Paths.get(jreDir, "lib", "ext") + File.separator + "*";
        extensionClasspathEntry = new WildcardEntry(jreExtPath);
    }

    private static String getJreDir(String jreOption) {
        if (Objects.nonNull(jreOption) && Files.exists(Paths.get(jreOption))) {
            return jreOption;
        }
        if (Files.exists(Paths.get("./jre"))) {
            return "./jre";
        }
        String javaHome = System.getenv("JAVA_HOME");
        if (Objects.nonNull(javaHome)) {
            return Paths.get(javaHome, "jre").toString();
        }
        throw new RuntimeException("Can not find JRE folder!");
    }

    public byte[] readClass(String className) throws Exception {
        className = className + ".class";
        try {
            return bootstrapClasspathEntry.readClass(className);
        } catch (Exception e) {
            // ignored
        }
        try {
            return extensionClasspathEntry.readClass(className);
        } catch (Exception e) {
            // ignored
        }
        return userClasspathEntry.readClass(className);
    }


}
